/*
 * Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package taskgraph.tasks.data;

/**
 * Holds a group of static classes for buffering native values.
 * 
 * @author Armando Blancas
 */
class NativeBuffers {
	
	private static int BUFFER_SIZE = 1024;
	
	/**
	 * Gets the default buffer size.
	 * 
	 * The initial default size is 1024.
	 * 
	 * @return The default buffer size.
	 */
	static int getBufferSize() {
		return BUFFER_SIZE;
	}

	/**
	 * Sets a new default buffer size.
	 * 
	 * The initial default size is 1024.
	 * 
	 * @param size The new default buffer size.
	 */
	static void setBufferSize(int size) {
		if (size <= 0)
			throw new IllegalArgumentException("size <= 0");
		BUFFER_SIZE = size;
	}

	/**
	 * A dynamic container of native boolean values.
	 * 
	 * <p>The internal buffer will grow to accommodate new values, reallocating
	 * and copying the filled buffer as necessary. New allocations will double
	 * the current size of the internal buffer. Values entered into the buffer 
	 * are returned in a newly allocated array that will exactly hold them all.
	 * 
	 * @author Armando Blancas
	 */
	static class BooleanBuffer {
		
		private boolean[] buffer;
		private int position;
		
		/**
		 * Default constructor.
		 */
		BooleanBuffer() {
			this(BUFFER_SIZE);
		}
		
		/**
		 * Constructor with initial buffer size.
		 * 
		 * @param size The initial size of the buffer.
		 */
		BooleanBuffer(int size) {
			if (size <= 0)
				throw new IllegalArgumentException("size <= 0");
			buffer = new boolean[size];
		}
		
		/**
		 * Adds a value to the buffer.
		 * 
		 * @param value A {@code boolean} value.
		 */
		void add(boolean value) {
			if (position == buffer.length) {
				boolean[] newbuffer = new boolean[position * 2];
				System.arraycopy(buffer, 0, newbuffer, 0, position);
				buffer = newbuffer;
			}
			buffer[position++] = value;
		}
		
		/**
		 * Gets the contents of this buffer in a new array of exact capacity.
		 * 
		 * @return A native {@code boolean} array with the buffer data.
		 */
		boolean[] toArray() {
			boolean[] result = new boolean[position];
			System.arraycopy(buffer, 0, result, 0, position);
			return result;
		}
		
		/**
		 * Copies the buffered values to the passed array.
		 * 
		 * @return The passed array or a new one if the buffer won't fit.
		 */
		boolean[] toArray(boolean[] array) {
			if (array.length < position) {
				array = new boolean[position];
			}
			System.arraycopy(buffer, 0, array, 0, position);
			return array;
		}
		
		/**
		 * Gets the number of values added to the buffer.
		 * 
		 * @return The current count.
		 */
		int getCount() {
			return position;
		}
		
	}
	
	
	/**
	 * A dynamic container of native byte values.
	 * 
	 * <p>The internal buffer will grow to accommodate new values, reallocating
	 * and copying the filled buffer as necessary. New allocations will double
	 * the current size of the internal buffer. Values entered into the buffer 
	 * are returned in a newly allocated array that will exactly hold them all.
	 * 
	 * @author Armando Blancas
	 */
	static class ByteBuffer {
		
		private byte[] buffer;
		private int position;
		
		/**
		 * Default constructor.
		 */
		ByteBuffer() {
			this(BUFFER_SIZE);
		}
		
		/**
		 * Constructor with initial buffer size.
		 * 
		 * @param size The initial size of the buffer.
		 */
		ByteBuffer(int size) {
			if (size <= 0)
				throw new IllegalArgumentException("size <= 0");
			buffer = new byte[size];
		}
		
		/**
		 * Adds a value to the buffer.
		 * 
		 * @param value A {@code byte} value.
		 */
		void add(byte value) {
			if (position == buffer.length) {
				byte[] newbuffer = new byte[position * 2];
				System.arraycopy(buffer, 0, newbuffer, 0, position);
				buffer = newbuffer;
			}
			buffer[position++] = value;
		}
		
		/**
		 * Gets the contents of this buffer in a new array of exact capacity.
		 * 
		 * @return A native {@code byte} array with the buffer data.
		 */
		byte[] toArray() {
			byte[] result = new byte[position];
			System.arraycopy(buffer, 0, result, 0, position);
			return result;
		}

		/**
		 * Copies the buffered values to the passed array.
		 * 
		 * @return The passed array or a new one if the buffer won't fit.
		 */
		byte[] toArray(byte[] array) {
			if (array.length < position) {
				array = new byte[position];
			}
			System.arraycopy(buffer, 0, array, 0, position);
			return array;
		}
		
		/**
		 * Gets the number of values added to the buffer.
		 * 
		 * @return The current count.
		 */
		int getCount() {
			return position;
		}
		
	}
	
	/**
	 * A dynamic container of native short integer values.
	 * 
	 * <p>The internal buffer will grow to accommodate new values, reallocating
	 * and copying the filled buffer as necessary. New allocations will double
	 * the current size of the internal buffer. Values entered into the buffer 
	 * are returned in a newly allocated array that will exactly hold them all.
	 * 
	 * @author Armando Blancas
	 */
	static class ShortBuffer {
		
		private short[] buffer;
		private int position;
		
		/**
		 * Default constructor.
		 */
		ShortBuffer() {
			this(BUFFER_SIZE);
		}
		
		/**
		 * Constructor with initial buffer size.
		 * 
		 * @param size The initial size of the buffer.
		 */
		ShortBuffer(int size) {
			if (size <= 0)
				throw new IllegalArgumentException("size <= 0");
			buffer = new short[size];
		}
		
		/**
		 * Adds a value to the buffer.
		 * 
		 * @param value A {@code short} value.
		 */
		void add(short value) {
			if (position == buffer.length) {
				short[] newbuffer = new short[position * 2];
				System.arraycopy(buffer, 0, newbuffer, 0, position);
				buffer = newbuffer;
			}
			buffer[position++] = value;
		}
		
		/**
		 * Gets the contents of this buffer in a new array of exact capacity.
		 * 
		 * @return A native {@code short} array with the buffer data.
		 */
		short[] toArray() {
			short[] result = new short[position];
			System.arraycopy(buffer, 0, result, 0, position);
			return result;
		}

		/**
		 * Copies the buffered values to the passed array.
		 * 
		 * @return The passed array or a new one if the buffer won't fit.
		 */
		short[] toArray(short[] array) {
			if (array.length < position) {
				array = new short[position];
			}
			System.arraycopy(buffer, 0, array, 0, position);
			return array;
		}
		
		/**
		 * Gets the number of values added to the buffer.
		 * 
		 * @return The current count.
		 */
		int getCount() {
			return position;
		}
		
	}
	
	/**
	 * A dynamic container of native integer values.
	 * 
	 * <p>The internal buffer will grow to accommodate new values, reallocating
	 * and copying the filled buffer as necessary. New allocations will double
	 * the current size of the internal buffer. Values entered into the buffer 
	 * are returned in a newly allocated array that will exactly hold them all.
	 * 
	 * @author Armando Blancas
	 */
	static class IntBuffer {
		
		private int[] buffer;
		private int position;
		
		/**
		 * Default constructor.
		 */
		IntBuffer() {
			this(BUFFER_SIZE);
		}
		
		/**
		 * Constructor with initial buffer size.
		 * 
		 * @param size The initial size of the buffer.
		 */
		IntBuffer(int size) {
			if (size <= 0)
				throw new IllegalArgumentException("size <= 0");
			buffer = new int[size];
		}
		
		/**
		 * Adds a value to the buffer.
		 * 
		 * @param value A {@code int} value.
		 */
		void add(int value) {
			if (position == buffer.length) {
				int[] newbuffer = new int[position * 2];
				System.arraycopy(buffer, 0, newbuffer, 0, position);
				buffer = newbuffer;
			}
			buffer[position++] = value;
		}
		
		/**
		 * Gets the contents of this buffer in a new array of exact capacity.
		 * 
		 * @return A native {@code int} array with the buffer data.
		 */
		int[] toArray() {
			int[] result = new int[position];
			System.arraycopy(buffer, 0, result, 0, position);
			return result;
		}

		/**
		 * Copies the buffered values to the passed array.
		 * 
		 * @return The passed array or a new one if the buffer won't fit.
		 */
		int[] toArray(int[] array) {
			if (array.length < position) {
				array = new int[position];
			}
			System.arraycopy(buffer, 0, array, 0, position);
			return array;
		}
		
		/**
		 * Gets the number of values added to the buffer.
		 * 
		 * @return The current count.
		 */
		int getCount() {
			return position;
		}
		
	}
	
	/**
	 * A dynamic container of native long integer values.
	 * 
	 * <p>The internal buffer will grow to accommodate new values, reallocating
	 * and copying the filled buffer as necessary. New allocations will double
	 * the current size of the internal buffer. Values entered into the buffer 
	 * are returned in a newly allocated array that will exactly hold them all.
	 * 
	 * @author Armando Blancas
	 */
	static class LongBuffer {
		
		private long[] buffer;
		private int position;
		
		/**
		 * Default constructor.
		 */
		LongBuffer() {
			this(BUFFER_SIZE);
		}
		
		/**
		 * Constructor with initial buffer size.
		 * 
		 * @param size The initial size of the buffer.
		 */
		LongBuffer(int size) {
			if (size <= 0)
				throw new IllegalArgumentException("size <= 0");
			buffer = new long[size];
		}
		
		/**
		 * Adds a value to the buffer.
		 * 
		 * @param value A {@code long} value.
		 */
		void add(long value) {
			if (position == buffer.length) {
				long[] newbuffer = new long[position * 2];
				System.arraycopy(buffer, 0, newbuffer, 0, position);
				buffer = newbuffer;
			}
			buffer[position++] = value;
		}
		
		/**
		 * Gets the contents of this buffer in a new array of exact capacity.
		 * 
		 * @return A native {@code long} array with the buffer data.
		 */
		long[] toArray() {
			long[] result = new long[position];
			System.arraycopy(buffer, 0, result, 0, position);
			return result;
		}

		/**
		 * Copies the buffered values to the passed array.
		 * 
		 * @return The passed array or a new one if the buffer won't fit.
		 */
		long[] toArray(long[] array) {
			if (array.length < position) {
				array = new long[position];
			}
			System.arraycopy(buffer, 0, array, 0, position);
			return array;
		}
		
		/**
		 * Gets the number of values added to the buffer.
		 * 
		 * @return The current count.
		 */
		int getCount() {
			return position;
		}
		
	}
	
	/**
	 * A dynamic container of native float values.
	 * 
	 * <p>The internal buffer will grow to accommodate new values, reallocating
	 * and copying the filled buffer as necessary. New allocations will double
	 * the current size of the internal buffer. Values entered into the buffer 
	 * are returned in a newly allocated array that will exactly hold them all.
	 * 
	 * @author Armando Blancas
	 */
	static class FloatBuffer {
		
		private float[] buffer;
		private int position;
		
		/**
		 * Default constructor.
		 */
		FloatBuffer() {
			this(BUFFER_SIZE);
		}
		
		/**
		 * Constructor with initial buffer size.
		 * 
		 * @param size The initial size of the buffer.
		 */
		FloatBuffer(int size) {
			if (size <= 0)
				throw new IllegalArgumentException("size <= 0");
			buffer = new float[size];
		}
		
		/**
		 * Adds a value to the buffer.
		 * 
		 * @param value A {@code float} value.
		 */
		void add(float value) {
			if (position == buffer.length) {
				float[] newbuffer = new float[position * 2];
				System.arraycopy(buffer, 0, newbuffer, 0, position);
				buffer = newbuffer;
			}
			buffer[position++] = value;
		}
		
		/**
		 * Gets the contents of this buffer in a new array of exact capacity.
		 * 
		 * @return A native {@code float} array with the buffer data.
		 */
		float[] toArray() {
			float[] result = new float[position];
			System.arraycopy(buffer, 0, result, 0, position);
			return result;
		}

		/**
		 * Copies the buffered values to the passed array.
		 * 
		 * @return The passed array or a new one if the buffer won't fit.
		 */
		float[] toArray(float[] array) {
			if (array.length < position) {
				array = new float[position];
			}
			System.arraycopy(buffer, 0, array, 0, position);
			return array;
		}
		
		/**
		 * Gets the number of values added to the buffer.
		 * 
		 * @return The current count.
		 */
		int getCount() {
			return position;
		}
		
	}
	
	/**
	 * A dynamic container of native double values.
	 * 
	 * <p>The internal buffer will grow to accommodate new values, reallocating
	 * and copying the filled buffer as necessary. New allocations will double
	 * the current size of the internal buffer. Values entered into the buffer 
	 * are returned in a newly allocated array that will exactly hold them all.
	 * 
	 * @author Armando Blancas
	 */
	static class DoubleBuffer {
		
		private double[] buffer;
		private int position;
		
		/**
		 * Default constructor.
		 */
		DoubleBuffer() {
			this(BUFFER_SIZE);
		}
		
		/**
		 * Constructor with initial buffer size.
		 * 
		 * @param size The initial size of the buffer.
		 */
		DoubleBuffer(int size) {
			if (size <= 0)
				throw new IllegalArgumentException("size <= 0");
			buffer = new double[size];
		}
		
		/**
		 * Adds a value to the buffer.
		 * 
		 * @param value A {@code double} value.
		 */
		void add(double value) {
			if (position == buffer.length) {
				double[] newbuffer = new double[position * 2];
				System.arraycopy(buffer, 0, newbuffer, 0, position);
				buffer = newbuffer;
			}
			buffer[position++] = value;
		}
		
		/**
		 * Gets the contents of this buffer in a new array of exact capacity.
		 * 
		 * @return A native {@code boolean} array with the buffer data.
		 */
		double[] toArray() {
			double[] result = new double[position];
			System.arraycopy(buffer, 0, result, 0, position);
			return result;
		}

		/**
		 * Copies the buffered values to the passed array.
		 * 
		 * @return The passed array or a new one if the buffer won't fit.
		 */
		double[] toArray(double[] array) {
			if (array.length < position) {
				array = new double[position];
			}
			System.arraycopy(buffer, 0, array, 0, position);
			return array;
		}
		
		/**
		 * Gets the number of values added to the buffer.
		 * 
		 * @return The current count.
		 */
		int getCount() {
			return position;
		}
		
	}
	
}
